/**************************************************************************************************
 * light library                                                                                  *
 * Copyright © 2012 David Kretzmer                                                                *
 *                                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software  *
 * and associated documentation files (the "Software"), to deal in the Software without           *
 * restriction,including without limitation the rights to use, copy, modify, merge, publish,      *
 * distribute,sublicense, and/or sell copies of the Software, and to permit persons to whom the   *
 * Software is furnished to do so, subject to the following conditions:                           *
 *                                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies or       *
 * substantial portions of the Software.                                                          *
 *                                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING  *
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND     *
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, *
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.        *
 *                                                                                                *
 *************************************************************************************************/

#pragma once

#include "light/utility/type_traits.hpp"

#include <string>


namespace light
{
namespace string
{
	//=============================================================================================
	//
	//=============================================================================================
	namespace internal
	{
		template<bool Const, typename CharType>
		struct device_typedef
		{
			typedef ::std::basic_string<CharType> type;
		};

		template<typename CharType>
		struct device_typedef<true, CharType>
		{
			typedef ::std::basic_string<CharType> const type;
		};
	}

	template<typename CharType>
	class string_io_base
	{
	public:
		typedef typename remove_const<CharType>::type pure_type;
		typedef typename internal::device_typedef<is_const<CharType>::value, pure_type>::type device_type;

		// For compatibility to iterator_traits
		typedef ptrdiff_t difference_type;
		typedef pure_type value_type;
		typedef CharType* pointer;
		typedef CharType& reference;
		typedef ::std::random_access_iterator_tag iterator_category;

		string_io_base(string_io_base const &rhs) :
			m_dev(rhs.m_dev),
			m_index(rhs.m_index) {}

		string_io_base& operator = (string_io_base const &rhs)
		{
			m_dev = rhs.m_dev;
			m_index = rhs.m_index;

			return *this;
		}

		reference operator * () const
		{
			assert(m_index < m_dev->size());
			return (*m_dev)[m_index];
		}

		// Arithmetic operators
		string_io_base operator + (size_t offset)
		{
			return string_io_base(m_dev, m_index + offset);
		}

		string_io_base operator - (string_io_base const &rhs) const
		{
			assert(m_dev == rhs.m_dev);
			return string_io_base(m_dev, m_index - rhs.m_index);
		}

		string_io_base operator - (size_t offset) const
		{
			return string_io_base(m_dev, m_index - offset);
		}

		string_io_base& operator += (size_t offset)
		{
			m_index += offset;
			return *this;
		}

		string_io_base& operator -= (string_io_base const &rhs)
		{
			assert(m_dev == rhs.m_dev);
			m_index -= rhs.m_index;
			return *this;
		}

		string_io_base& operator -= (size_t offset)
		{
			m_index -= offset;
			return *this;
		}

		// Prefix operators
		string_io_base& operator ++ ()
		{
			++m_index;
			return *this;
		}

		string_io_base& operator -- ()
		{
			--m_index;
			return *this;
		}

		// Postfix operators
		string_io_base operator ++ (int)
		{
			string_io_base tmp(*this);
			++(*this);
			return tmp;
		}

		string_io_base operator -- (int)
		{
			string_io_base tmp(*this);
			--(*this);
			return tmp;
		}

		// Comparison
		bool operator == (string_io_base const &rhs) const
		{
			assert(m_dev == rhs.m_dev);
			return m_index == rhs.m_index;
		}

		bool operator != (string_io_base const &rhs) const
		{
			assert(m_dev == rhs.m_dev);
			return m_index != rhs.m_index;
		}

		// Cast
		operator size_t () const { return m_index; }



		void reserve(size_t size)
		{
			if(m_index + size >= m_dev->size())
				m_dev->resize(m_index + size + 1);
		}

		void flush() {}

	protected:
		device_type *m_dev;
		size_t m_index;


		// This constructor is only needed by the global functions 'begin', 'end' and 'make_reader'
		string_io_base(device_type *dev, size_t index) :
			m_dev(dev),
			m_index(index) {}

		template<typename Char>
		friend string_io_base<Char> begin(string_io_base<Char> &dev);

		template<typename Char>
		friend string_io_base<Char> end(string_io_base<Char> &dev);

		template<typename Char>
		friend string_io_base<Char const> make_reader(::std::basic_string<Char> const &dev, size_t index);

		template<typename Char>
		friend string_io_base<Char> make_readwriter(::std::basic_string<Char> &dev, size_t index);
	};



	template<typename CharType>
	string_io_base<CharType> begin(string_io_base<CharType> &dev)
	{
		return {dev.m_dev, 0};
	}

	template<typename CharType>
	string_io_base<CharType> end(string_io_base<CharType> &dev)
	{
		return {dev.m_dev, dev.m_dev->size()};
	}

	template<typename CharType>
	string_io_base<CharType const> make_reader(::std::basic_string<CharType> const &dev, size_t index = 0)
	{
		return {&dev, index};
	}


	template<typename CharType>
	string_io_base<CharType> make_readwriter(::std::basic_string<CharType> &dev, size_t index = 0)
	{
		return {&dev, index};
	}


} // namespace: string

using string::begin;
using string::end;
using string::make_reader;
using string::make_readwriter;

} // namespace: light
